1. Field of the Invention
The present invention generally relates to data processing and more particularly to debugging code.
2. Description of the Related Art
A programmer develops a software program by producing and entering source code into files using a text editor program. The computer then creates an executable program by translating the source code into machine code. The machine code is the rudimentary instructions understood by a computer. Illustratively, the foregoing software development process is accomplished by running a series of programs. These programs typically include a compiler for translating the source code into machine code and a linker to link the machine code together to form a program.
When developing computer software, it is necessary to perform a function termed “debugging”. Simply stated, debugging is performed by a computer programmer to locate and identify errors in a program under development. Typically, a programmer uses another computer program commonly known as a “debugger” to debug a program under development. An effective debugger program is necessary for rapid and efficient development of software and typically provides functions including run-to-cursor, step into, step over, step outof, breakpoints and the like.
One primary operation supported by conventional debuggers is a “step” function, which permits a computer programmer to process instructions (also known as “statements”) in a computer program one-by-one, and see the results upon completion of each instruction. While the step operation provides a programmer with a large amount of information about a program during its execution, stepping through hundreds or thousands of program instructions can be extremely tedious and time consuming, and may require a programmer to step through many program instructions that are known to be error-free before a set of instructions to be analyzed are executed.
A second operation supported by conventional debuggers is a breakpoint operation, which permits a computer programmer to identify with a “breakpoint” a precise instruction for which it is desired to halt execution of a computer program during execution. As a result, when a computer program is executed by a debugger, the program executes in a normal fashion until a breakpoint is reached, and then stops execution and displays the results of the computer program to the programmer for analysis. Most breakpoints supported by conventional debuggers are unconditional, meaning that once such a breakpoint is reached, execution of the program is always halted. Some debuggers also support the use of conditional breakpoints, which only halt execution of a program when a variable used by the program is set to a predetermined value at the time such a breakpoint is reached.
Another conventional debugging operation, the run-to-cursor function, allows a user to place a cursor on a selected statement and then execute the program from a current stopped position to the cursor. When execution reaches the line at which the cursor is located, the debugger gains control. In this way, the user may observe the effects of running the portion of code from the current stopped position to the cursor position. If unexpected results are identified, the user has successfully located the source of a problem and may then take remedial steps to correct the problem.
Typically, step operations, breakpoints, and run-to functions are used together to simplify the debugging process. For example, a common debugging operation is to set a breakpoint at the beginning of a desired set of instructions to be analyzed, and then begin executing the program. Once the breakpoint is reached, the program is halted, and the programmer then steps through the desired set of instructions line-by-line using the step operation, or runs to a selected line number. Consequently, a programmer is able to quickly isolate and analyze a particular set of instructions without having to step through irrelevant portions of a computer program.
While the foregoing conventional debugging tools are helpful, they are also substantially limited in certain situations. In particular, object-oriented programming and other highly modular languages present some unique debugging problems not easily handled by conventional techniques.
With an object-oriented programming language, for example, a program is constructed from a number of “objects”, each of which includes data and/or one or more sets of instructions (often referred to as “routines” or “methods”) that define specific operations that can be performed on the data. A large number of objects may be used to build a computer program, with each object interacting with other objects in the computer program to perform desired operations.
One characteristic of an object-oriented programming language that can make debugging problematic is that object classes can have many subclasses. As a result, it can be tedious to set breakpoints where you need them. Consider the code shown in TABLE I.
TABLE I25while(i < 40){26 .27 .28 widget = this.getWidget(tool.foo1( ). widget.foo2( )).:29 widget.run( ):3031}Assume that the programmer has stopped somewhere in the code prior to line 28 and wants to “step into” the method “widget.foo2()” called at this point, i.e., from the “main” function. Using conventional debugging techniques the programmer must first get to line 28 and then use the “step into” function. The problem with this approach is that it will cause the programmer to step into “tool.foo1()” before getting to the desired method. As an alternative, the user may set the cursor on the statement containing the method “widget.foo2()” and then employ the “run-to-cursor” function to arrive at the statement. Upon arriving at the statement, the user must employ the “step into” function. Accordingly, this approach is labor intensive in that it requires the user to perform multiple steps. As yet another alternative, the programmer must manually identify the portion of the source code containing the method, identify the method within the code, set a breakpoint on each entry point to that method, and then after encountering the desired breakpoint manually remove all the remaining breakpoints. Such an approach is burdensome and error prone.
Another problem that is especially pronounced in highly modular languages (such as object-oriented programming) results from the fact that some instructions in a computer program are executed fairly often for different purposes, and may result in many needless stoppages before a desired stoppage is encountered. More specifically, since some methods are called by many different objects, placing a conventional breakpoint on a method may result in hundreds of unwanted stoppages prior to occurrence of a desired stoppage. This may be illustrated with the following code: foo(obj1.bar(),obj2.bar()); where obj1 and obj2 are both objects of the same class. Assume the programmer desires to run into obj2.bar(). If the programmer simply examines the class hierarchy and places a breakpoint on each instance of obj2.bar(), the program will be halted even when obj1.bar() is encountered.
Therefore, there is a need for improvements in debugging computer programs to simplify and facilitating the debugging process, particularly in object-oriented environments.